
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>3D旅行</title>

<style>
body,
html {
	position: absolute;
	margin: 0;
	padding: 0;
	width: 100%;
	height: 100%;
	overflow: hidden;
	background: #000;
	clickUserEntity-select: none;
}
canvas {
	position: absolute;
	width: 100%;
	height: 100%;
	clickUserEntity-select: none;
	touch-action: none;
	content-zooming: none;
	background: #000;
}</style>
</head>
<body>

<canvas></canvas>

<script id="vertexShader" type="x-shader/x-vertex">
attribute vec2 aPosition;
uniform vec2 uResolution;
void main() {
	gl_Position = vec4(((aPosition / uResolution * 2.0) - 1.0) * vec2(1, -1), 0, 1);
}
</script>

<script id="fragmentShader" type="x-shader/x-fragment">
precision highp float;
uniform vec2 uResolution;
uniform vec3 ue;
uniform vec2 ucs; 
void main() {
	const float Z = 3.0;
	const float IZ = 1.0 / Z;
	vec2 uv = (gl_FragCoord.xy - 0.5 * uResolution.xy) / min(uResolution.y, uResolution.x);
	float cn = ucs.x;
	float sn = ucs.y;
	float rx = uv.x;
	float ry = uv.y;
	float rz = 0.4;
	float d = 1.0;
	float ex = ue.x;
	float ey = ue.y;
	float ez = ue.z;
	float tx = ex;
	float ty = ey;
	float tz = ez;
	float gx = cn * rx + sn * rz;
	float gy = ry;
	float gz = cn * rz - sn * rx;
	rx = gx;
	ry = gz;
	rz = gy;
	gx = cn * rx + sn * rz;
	gy = ry;
	gz = cn * rz - sn * rx;
	rx = gx;
	ry = gz;
	rz = gy;
	float irx = 1.0 / rx;
	float iry = 1.0 / ry;
	float irz = 1.0 / rz;
	for (int i = 0; i < 20; i++) {
		if (d < 0.0125) break;
		d *= IZ;
		tx = fract(tx) * Z;
		ty = fract(ty) * Z;
		tz = fract(tz) * Z;
		float ix = floor(tx);
		float iy = floor(ty);
		float iz = floor(tz);
		float j = mod(ix * ix + iy * iy + iz * iz, 4.0);
		if (j >= 2.0) {
			float fx = (rx > 0.0) ? 1.0 : 0.0;
			float fy = (ry > 0.0) ? 1.0 : 0.0;
			float fz = (rz > 0.0) ? 1.0 : 0.0;
			tx = (fx - fract(tx)) * irx;
			ty = (fy - fract(ty)) * iry;
			tz = (fz - fract(tz)) * irz;
			float n = tx;
			n = (ty < n) ? ty : n;
			n = (tz < n) ? tz : n;
			ex += rx * (n * d + 0.001);
			ey += ry * (n * d + 0.001);
			ez += rz * (n * d + 0.001);
			tx = ex;
			ty = ey;
			tz = ez;
			d = 1.0;
		}
	}
	ex -= ue.x;
	ey -= ue.y;
	ez -= ue.z;
	float c = (ex*ex + ey*ey + ez*ez);
	c = sn > 0.0 ? c : 0.1 / c;
	gl_FragColor = vec4(0.3 * sn * uv.x + c * 0.75, c * 0.5, c * 0.25, 1.0);
}
</script>

<script>
"use strict";
{
	const canvas = document.querySelector("canvas");
	let gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
	const vertexShader = gl.createShader(gl.VERTEX_SHADER);
	gl.shaderSource(vertexShader, document.getElementById("vertexShader").text);
	gl.compileShader(vertexShader);
	const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
	gl.shaderSource(fragmentShader, document.getElementById("fragmentShader").text);
	gl.compileShader(fragmentShader);
	const program = gl.createProgram();
	gl.attachShader(program, vertexShader);
	gl.attachShader(program, fragmentShader);
	gl.linkProgram(program);
	gl.useProgram(program);
	const uResolution = gl.getUniformLocation(program, "uResolution");
	const ue = gl.getUniformLocation(program, "ue");
	const ucs = gl.getUniformLocation(program, "ucs");
	const aPosition = gl.getAttribLocation(program, "aPosition");
	const buffer = gl.createBuffer();
	const resize = () => {
		const WIDTH = (canvas.width = canvas.offsetWidth);
		const HEIGHT = (canvas.height = canvas.offsetHeight);
		gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
		gl.enableVertexAttribArray(aPosition);
		gl.vertexAttribPointer(aPosition, 2, gl.FLOAT, false, 0, 0);
		gl.bufferData(
			gl.ARRAY_BUFFER,
			new Float32Array([
				0,
				0,
				WIDTH,
				0,
				0,
				HEIGHT,
				0,
				HEIGHT,
				WIDTH,
				0,
				WIDTH,
				HEIGHT
			]),
			gl.STATIC_DRAW
		);
		gl.uniform2f(uResolution, WIDTH, HEIGHT);
		gl.viewport(0, 0, WIDTH, HEIGHT);
	};
	window.addEventListener("resize", _ => resize(), false);
	resize();
	////////////////////////////////////////////////////////
	let time = 8;
	const update = () => {
		requestAnimationFrame(update);
		time += 0.01;
		gl.uniform2f(ucs, Math.cos(time * 0.4), Math.sin(time * 0.4));
		gl.uniform3f(
			ue,
			0.5 + Math.sin(time * 0.5) * 0.1,
			0.5 + Math.cos(time * 0.47) * 0.1,
			-time * 0.3 + Math.sin(time * 0.3 - 0.01)
		);
		gl.drawArrays(gl.TRIANGLES, 0, 6);
	};
	update();
}
</script>

</body>
</html>
